/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include <gtest/gtest.h>
#include <test_utilities/test_base.h>

#include "devmon.h"
#include "device/driver.h"
#include "device/manager.h"
#include "device/test_driver.h"
#include "discovery/app_schema.h"
#include "discovery/csr_utils.h"
#include "discovery/object_group.h"
#include "discovery/root_interface.h"
#include "discovery/root_object.h"

using namespace devmon;

const std::string TEST_SERVICE_NAME = "bmc.kepler.test_root_interface";

class RootInterfaceTest : public mc::test::TestWithEngine {
public:
    static void SetUpTestSuite() {
        TestWithEngine::SetUpTestSuite();
        m_test_service = mc::make_shared<devmon::devmon_service>(TEST_SERVICE_NAME);
        m_test_service->init({{"driver_path", "./opt/bmc/drivers"}});
        m_test_service->start();
        create_driver();
        std::unordered_map<std::string, std::string> app_paths = {
            {"general_path", "../tests/tests_data/apps/"}};
        app_schema::get_instance().init(app_paths);
    }

    static void TearDownTestSuite() {
        m_test_service->stop();
        TestWithEngine::TearDownTestSuite();
    }

    void SetUp() override {
        TestWithEngine::SetUp();
        object_group_data::reset_for_test();
        create_driver();
    }

    void TearDown() override {
        // 先通过 remove_device_object 清理 device_topo 中的设备对象映射
        // 这样可以避免双重删除（remove_device_object 会调用 unregister_object）
        try {
            auto& topo = device_topo::get_instance();
            std::vector<std::string> positions = {"0101", "0202", "0303", "0404", "0505", "0606"};
            for (const auto& pos : positions) {
                try {
                    topo.remove_device_object(pos);
                } catch (...) {
                    // 忽略单个清理错误
                }
            }
        } catch (...) {
            // 忽略清理错误
        }
        
        // 然后清理剩余的对象（ObjectGroup 对象等）
        auto& object_table = m_test_service->get_object_table();
        for (int retry = 0; retry < 3; ++retry) {
            std::vector<std::string> paths_to_remove;
            object_table.query_object(
                mc::db::query_builder(), [&paths_to_remove](mc::db::object_base& obj) {
                    try {
                        auto& abstract_obj = static_cast<mc::engine::abstract_object&>(obj);
                        auto  object_name  = std::string(abstract_obj.get_object_name());
                        auto  object_path  = std::string(abstract_obj.get_object_path());
                        // 保留根对象，清理测试创建的对象
                        if (object_name != "Devmon" && object_name != "DevmonTopo" &&
                            object_name != "DevTopology" && object_name != "ObjectGroupRoot" &&
                            object_path != "/bmc/dev") {
                            // 清理 Systems 下的设备对象和 ObjectGroup 对象
                            if (object_path.find("/bmc/dev/Systems/") == 0 ||
                                object_path.find("/bmc/dev/ObjectGroup/") == 0) {
                                paths_to_remove.push_back(object_path);
                            }
                        }
                    } catch (...) {
                    }
                    return true;
                });

            if (paths_to_remove.empty()) {
                break;
            }

            std::sort(paths_to_remove.begin(), paths_to_remove.end(),
                      [](const std::string& a, const std::string& b) {
                          return a.length() > b.length();
                      });

            for (const auto& path : paths_to_remove) {
                try {
                    bool object_exists = false;
                    object_table.query_object(
                        mc::db::query_builder(),
                        [&object_exists, &path](mc::db::object_base& obj) {
                            try {
                                auto& abstract_obj = static_cast<mc::engine::abstract_object&>(obj);
                                const auto object_path = std::string(abstract_obj.get_object_path());
                                if (object_path == path) {
                                    object_exists = true;
                                    return false;
                                }
                            } catch (...) {
                            }
                            return true;
                        });
                    if (object_exists) {
                        m_test_service->unregister_object(path);
                    }
                } catch (...) {
                }
            }
        }
        
        object_group_data::reset_for_test();
        mc::expr::func_collection::get_instance().clear();
        device_driver_factory::get_instance().remove_device_driver("test_driver");
        TestWithEngine::TearDown();
    }

    static void create_driver() {
        device_drivers drivers;
        drivers.clear();
        drivers.reserve(3);
        auto               test_driver_ptr     = std::make_unique<test::test_driver>();
        static std::string pcie_nic_card_name  = "PCIeNicCard";
        static std::string network_port_name   = "NetworkPort";
        static std::string optical_module_name = "OpticalModule";
        auto               pcie_nic_card       = test_driver_ptr->create(pcie_nic_card_name);
        auto               network_port        = test_driver_ptr->create(network_port_name);
        auto               optical_module      = test_driver_ptr->create(optical_module_name);
        drivers.emplace_back(pcie_nic_card);
        drivers.emplace_back(network_port);
        drivers.emplace_back(optical_module);
        device_driver_factory::get_instance().set_device_driver("test_driver", &drivers);
        (void)test_driver_ptr.release();
    }

    devmon_service* get_service() const {
        return m_test_service.get();
    }

protected:
    static mc::shared_ptr<devmon::devmon_service> m_test_service;
};

mc::shared_ptr<devmon::devmon_service> RootInterfaceTest::m_test_service;

// ========================= add_device 测试 =========================

// 测试 add_device 正常流程
TEST_F(RootInterfaceTest, test_add_device_success) {
    auto&        object_table = m_test_service->get_object_table();
    auto         devmon_obj   = object_table.find_object(mc::engine::by_object_name::field == "Devmon");
    auto*        devmon_abstract = static_cast<mc::engine::abstract_object*>(devmon_obj.get());
    auto root_iface_raw = devmon_abstract->get_interface("root");
    ASSERT_NE(root_iface_raw, nullptr);
    auto* root_iface = static_cast<root_interface*>(root_iface_raw);

    mc::dict connector;
    connector["SystemId"]      = 1;
    connector["GroupPosition"] = "0101";
    connector["Slot"]          = 1;
    connector["Buses"]         = mc::variants{"I2c_1", "I2c_2"};

    std::string csr_file = "./tests_data/csr/topology.sr";
    EXPECT_NO_THROW(root_iface->add_device(csr_file, connector));

    // 验证对象组已创建
    auto obj_group = object_table.find_object(mc::engine::by_object_name::field == "0101");
    EXPECT_NE(obj_group, nullptr);
}

// 测试 add_device 文件不存在的情况
TEST_F(RootInterfaceTest, test_add_device_file_not_exist) {
    auto&        object_table = m_test_service->get_object_table();
    auto         devmon_obj   = object_table.find_object(mc::engine::by_object_name::field == "Devmon");
    auto*        devmon_abstract = static_cast<mc::engine::abstract_object*>(devmon_obj.get());
    auto root_iface_raw = devmon_abstract->get_interface("root");
    ASSERT_NE(root_iface_raw, nullptr);
    auto* root_iface = static_cast<root_interface*>(root_iface_raw);

    mc::dict connector;
    connector["SystemId"]      = 1;
    connector["GroupPosition"] = "0101";
    connector["Slot"]          = 1;

    std::string csr_file = "./tests_data/csr/nonexistent_file.sr";
    EXPECT_THROW(root_iface->add_device(csr_file, connector), mc::invalid_arg_exception);
}

// 测试 add_device 空 position 的情况（应该抛出异常）
TEST_F(RootInterfaceTest, test_add_device_empty_position) {
    auto&        object_table = m_test_service->get_object_table();
    auto         devmon_obj   = object_table.find_object(mc::engine::by_object_name::field == "Devmon");
    auto*        devmon_abstract = static_cast<mc::engine::abstract_object*>(devmon_obj.get());
    auto root_iface_raw = devmon_abstract->get_interface("root");
    ASSERT_NE(root_iface_raw, nullptr);
    auto* root_iface = static_cast<root_interface*>(root_iface_raw);

    mc::dict connector;
    connector["SystemId"] = 1;
    connector["Slot"]     = 1;
    // 不设置 GroupPosition，position 会是空字符串

    std::string csr_file = "./tests_data/csr/topology.sr";
    // 空 position 应该抛出异常
    EXPECT_THROW(root_iface->add_device(csr_file, connector), std::exception);
}

// ========================= remove_device 测试 =========================

// 测试 remove_device 正常流程
TEST_F(RootInterfaceTest, test_remove_device_success) {
    auto&        object_table = m_test_service->get_object_table();
    auto         devmon_obj   = object_table.find_object(mc::engine::by_object_name::field == "Devmon");
    auto*        devmon_abstract = static_cast<mc::engine::abstract_object*>(devmon_obj.get());
    auto root_iface_raw = devmon_abstract->get_interface("root");
    ASSERT_NE(root_iface_raw, nullptr);
    auto* root_iface = static_cast<root_interface*>(root_iface_raw);

    // 先添加设备
    mc::dict connector;
    connector["SystemId"]      = 3;  // 使用不同的 SystemId 避免路径冲突
    connector["GroupPosition"] = "0303";
    connector["Slot"]          = 1;
    connector["Buses"]         = mc::variants{"I2c_1", "I2c_2"};

    std::string csr_file = "./tests_data/csr/topology.sr";
    root_iface->add_device(csr_file, connector);

    // 验证对象组已创建
    auto obj_group = object_table.find_object(mc::engine::by_object_name::field == "0303");
    ASSERT_NE(obj_group, nullptr);

    // 移除设备
    std::map<std::string, std::string> context;
    EXPECT_NO_THROW(root_iface->remove_device(context, connector));

    // 验证对象组已被移除（可能需要查询确认）
}

// 测试 remove_device 空 position 的情况
TEST_F(RootInterfaceTest, test_remove_device_empty_position) {
    auto&        object_table = m_test_service->get_object_table();
    auto         devmon_obj   = object_table.find_object(mc::engine::by_object_name::field == "Devmon");
    auto*        devmon_abstract = static_cast<mc::engine::abstract_object*>(devmon_obj.get());
    auto root_iface_raw = devmon_abstract->get_interface("root");
    ASSERT_NE(root_iface_raw, nullptr);
    auto* root_iface = static_cast<root_interface*>(root_iface_raw);

    mc::dict connector;
    connector["SystemId"] = 1;
    connector["Slot"]     = 1;
    // 不设置 GroupPosition，position 会是空字符串

    std::map<std::string, std::string> context;
    // 空 position 应该直接返回，不抛异常
    EXPECT_NO_THROW(root_iface->remove_device(context, connector));
}

// 测试 remove_device 不存在的设备
TEST_F(RootInterfaceTest, test_remove_device_not_exist) {
    auto&        object_table = m_test_service->get_object_table();
    auto         devmon_obj   = object_table.find_object(mc::engine::by_object_name::field == "Devmon");
    auto*        devmon_abstract = static_cast<mc::engine::abstract_object*>(devmon_obj.get());
    auto root_iface_raw = devmon_abstract->get_interface("root");
    ASSERT_NE(root_iface_raw, nullptr);
    auto* root_iface = static_cast<root_interface*>(root_iface_raw);

    mc::dict connector;
    connector["SystemId"]      = 1;
    connector["GroupPosition"] = "9999";  // 不存在的 position
    connector["Slot"]          = 1;

    std::map<std::string, std::string> context;
    // 移除不存在的设备不应该抛异常
    EXPECT_NO_THROW(root_iface->remove_device(context, connector));
}

// 测试 remove_device 多次移除相同设备
TEST_F(RootInterfaceTest, test_remove_device_duplicate) {
    auto&        object_table = m_test_service->get_object_table();
    auto         devmon_obj   = object_table.find_object(mc::engine::by_object_name::field == "Devmon");
    auto*        devmon_abstract = static_cast<mc::engine::abstract_object*>(devmon_obj.get());
    auto root_iface_raw = devmon_abstract->get_interface("root");
    ASSERT_NE(root_iface_raw, nullptr);
    auto* root_iface = static_cast<root_interface*>(root_iface_raw);

    // 先添加设备
    mc::dict connector;
    connector["SystemId"]      = 4;  // 使用不同的 SystemId 避免路径冲突
    connector["GroupPosition"] = "0404";
    connector["Slot"]          = 1;
    connector["Buses"]         = mc::variants{"I2c_1", "I2c_2"};

    std::string csr_file = "./tests_data/csr/topology.sr";
    root_iface->add_device(csr_file, connector);

    // 移除设备
    std::map<std::string, std::string> context;
    EXPECT_NO_THROW(root_iface->remove_device(context, connector));

    // 再次移除相同设备不应该抛异常
    EXPECT_NO_THROW(root_iface->remove_device(context, connector));
}

// ========================= 边界情况测试 =========================

// 测试 add_device 使用不同的 connector 字段
TEST_F(RootInterfaceTest, test_add_device_with_optional_fields) {
    auto&        object_table = m_test_service->get_object_table();
    auto         devmon_obj   = object_table.find_object(mc::engine::by_object_name::field == "Devmon");
    auto*        devmon_abstract = static_cast<mc::engine::abstract_object*>(devmon_obj.get());
    auto root_iface_raw = devmon_abstract->get_interface("root");
    ASSERT_NE(root_iface_raw, nullptr);
    auto* root_iface = static_cast<root_interface*>(root_iface_raw);

    mc::dict connector;
    connector["SystemId"]      = 5;  // 使用不同的 SystemId 避免路径冲突
    connector["GroupPosition"] = "0505";
    connector["Slot"]          = 1;
    connector["Buses"]         = mc::variants{"I2c_1", "I2c_2"};
    connector["ManagerId"]     = "Manager1";
    connector["ChassisId"]     = "Chassis1";

    std::string csr_file = "./tests_data/csr/topology.sr";
    EXPECT_NO_THROW(root_iface->add_device(csr_file, connector));
}

// 测试 remove_device 使用 context 参数
TEST_F(RootInterfaceTest, test_remove_device_with_context) {
    auto&        object_table = m_test_service->get_object_table();
    auto         devmon_obj   = object_table.find_object(mc::engine::by_object_name::field == "Devmon");
    auto*        devmon_abstract = static_cast<mc::engine::abstract_object*>(devmon_obj.get());
    auto root_iface_raw = devmon_abstract->get_interface("root");
    ASSERT_NE(root_iface_raw, nullptr);
    auto* root_iface = static_cast<root_interface*>(root_iface_raw);

    // 先添加设备
    mc::dict connector;
    connector["SystemId"]      = 6;  // 使用不同的 SystemId 避免路径冲突
    connector["GroupPosition"] = "0606";
    connector["Slot"]          = 1;
    connector["Buses"]         = mc::variants{"I2c_1", "I2c_2"};

    std::string csr_file = "./tests_data/csr/topology.sr";
    root_iface->add_device(csr_file, connector);

    // 使用非空 context 移除设备
    std::map<std::string, std::string> context;
    context["key1"] = "value1";
    context["key2"] = "value2";

    EXPECT_NO_THROW(root_iface->remove_device(context, connector));
}
